查看原文
其他

Android 仿猿题库巧用Span实现填空题 & 技术分享资料下载

文艺的程序狗 鸿洋 2019-04-05

本文作者


作者:文艺的程序狗

链接:

http://www.jianshu.com/p/29605bceea86

本文由作者投稿发布。


关键字:填空题、SpanString 、Html、TextView、自定义ReplacementSpan、猿题库


1前言


关于填空题,每人都有自己实现的特点,比如这篇文章http://www.jianshu.com/p/3c8a1c987cea 这种基于PopupWindow或者是Dialog中转来把文字填入,当然也有选词填入的,但个人觉得这都不是真正意义上的填空题。


真正意义上的填空题应该可以能在文本下划线里输入文字,而且能满足比较好的扩展。


这一点我觉得猿题库做的比较好,所以这篇文章可以说是模仿猿题库填空题的效果,但是也有不同之处,效果的实现思路很大程度上借鉴了z56402344的AnswerDemo https://github.com/z56402344/AnswerDemo,在此对作者表示感谢。


高清无码图



怎么样,效果还是还可以吧,当然,你要选中加其它各种效果还是很容易扩展的,下面我将一步一步讲解实现的思路,心急的童鞋想直接看代码可以戳这里


👉特殊通道

https://github.com/TMLAndroid/FillBlankDemo


2臆想



如果你的设计师要求你实现这种效果,你大概想采用TextView +EditText的组合来实现,然后根据文本出现的下划线拆分,然后一个一个拼接,咦,怎么布局呢?


突然流畅的思路被卡住了!又水平又垂直的。可能你会想到了流式布局,阴郁的天气瞬间晴朗了起来!BUT,光性能先不说,比如我以后要加一个试题的复制功能,就不好扩展了,代码整洁性以及扩展性是所不能够忍受的!


那怎么搞?


下面将从两方面来讲


  • 需要实现的效果

  • 实现思路


PS:需要具备Spanned系列Api使用的基础,具体学习可以网上搜索博客

给大家找了一篇(可直接点击阅读):TextView 图文混排 & 炫酷的段落级Span解析



需要的效果


1. 自动识别特定带填空题的标识,并能够自定义样式

2. 填空题部分能够相应点击效果

3. 填空题部分能够输入文字

4. 输入完成之后可以把文本显示到下划线上面


需求写完一时爽,程序猿实现起来苦叫天。没办法,毕竟拿人钱财替人消灾,不行也得行,硬着头皮上!


实现思路


有点Android基础的都晓得TextView可以展示富文本信息,其实这些富文本信息的实现都是由Spanned系列拼接成的的SpannableStringBuilder,然后通过TextView.setText(CharSequence text)方法设置进去(说到富文本展示,还有一个方式是Html.fromHtml(String source)及其重载方法。


翻看源码,本质上的实现还是和Spanned系列拼接而成的,我这里采用的就是这种方式),这里我把填空题设置为自定义的<edit>标签,然后在Html. fromHtml(String source, int flags, Html.ImageGetter imageGetter, Html.TagHandler tagHandler)重载方法的第三个参数Html.TagHandler来处理我的自定义标签<edit>,替换成我想要的内容。


这里替换成我的自定义ReplacementSpan,这里可能很多人没有听说过这个类,本篇文章讲解实现思路,所以不打算深入讲解这个类的使用,这里简单介绍一下它的类的继承关系:



可以看到ReplacementSpan类继承自MetricAffectingSpan,而MetricAffectingSpan又继承自CharacterStyle,而我们熟知的许多下划线UnderlineSpan 背景 BackgroundColorSpan 都是继承该类。



这里我自定义ReplacementSpan,就可以实现下划线以及在下划线上显示用户输入文字的效果(这里在思路上已经实现了第1个和第4个效果)


TextView有个点击触摸事件方法setMovementMethod(MovementMethod movement),这里我们可以自定义MovementMethod来实现特定字符的点击效果(这里我们就实现了第2个效果),在点击的时候计算好填空题的位置和长宽,然后把EditText放置上去就可以输入文字了(这里实现第3个效果)


大体上解决思路就讲完了,具体到代码可能需要很多细节。


咦?搞什么,没有代码??没有贴代码吹啥牛逼!




这里我放一下关键类自定义ReplacementSpan,更多的细节我的代码已经上传gitHub,前面已经给出链接。

https://github.com/TMLAndroid/FillBlankDemo



/**
* Created by tangminglong on 17/10/19.
* 自定义Span,用来绘制填空题
*/

public class ReplaceSpan extends ReplacementSpan {
   private final Context context;
   public String mText = "";//保存的String
   private final Paint mPaint;
   public Object mObject;//回调中的任意对象
   private int textWidth = 80;//单词的宽度
   public OnClickListener mOnClick;
   public int id = 0;//回调中的对应Span的ID
   public ReplaceSpan(Context context,Paint paint) {
       this.context = context;
       mPaint = paint;
       textWidth = DensityUtils.dp2px(context,textWidth);
   }
   public void setDrawTextColor(int res){
       mPaint.setColor(context.getResources().getColor(res));
   }
   @Override
   public int getSize(@NonNull Paint paint,
               CharSequence text, int start,
               int end, Paint.FontMetricsInt fm)
{
       //返回自定义Span宽度
       return textWidth;
   }
   @Override
   public void draw(@NonNull Canvas canvas,
           CharSequence text, int start, int end,
           float x, int top, int y, int bottom,
           @NonNull Paint paint)
{
       float bottom1 =  paint.getFontMetrics().bottom;
       float y1 = y+bottom1;
       CharSequence ellipsize = TextUtils.ellipsize(mText,
               (TextPaint) paint, textWidth, TextUtils.TruncateAt.END);
       int width = (int)paint.measureText(ellipsize,0,ellipsize.length());
       width = (textWidth-width)/2;
       canvas.drawText(ellipsize,0,ellipsize.length(),
                           x+width, (float) y,mPaint);
       //需要填写的单词下方画线
       //这里bottom-1,是为解决有时候下划线超出canvas
       Paint linePaint = new Paint();
       linePaint.setColor(mPaint.getColor());
       linePaint.setStrokeWidth(2);
       canvas.drawLine(x, y1, x + textWidth, y1, linePaint);
   }
   public void onClick(TextView v, Spannable buffer,
           boolean isDown, int x, int y, int line, int off)
{
       if (mOnClick != null){
           mOnClick.OnClick(v,id,this);
       }
   }
   public  interface OnClickListener{
       void OnClick(TextView v, int id, ReplaceSpan span);
   }
}


最后


有时间的话我会整理出其他题型的适配,欢迎关注。

最近学习自定义View的进阶,扔物线的系列教程很不错,好东西和大家一起分享。

👉特殊通道

http://hencoder.com/overview/


推荐阅读:

1024 送你几个用起来很爽的Studio插件

Android 优雅展示填空题 拖拽完成选词


此外上次任玉刚老师做的Gradle直播分享的视频以及ppt资源可以下载了,包含:

  • Android学习路线和职业规划探讨ppt

  • Gradle开发入门ppt

  • 直播的视频

地址:https://pan.baidu.com/s/1eSbAJyq ,失效或者懒得复制,后台回复1108。




赞助商

Udacity 最近双十一活动,硅谷的技术课程最高可减1111元,感兴趣的朋友可以扫下方二维码领取优惠~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存